/**
 * 渲染各类型聊天的相关操作
 */
import { EllipsisOutlined } from '@ant-design/icons'
import { Tooltip, message } from 'antd'
import { copy } from '@/lib/utils'
import services from '@/services'
import {
  ChatRecordType as Type,
  type ChatRecordMap,
  type ChatRecord,
  type TextRecord,
} from '@/services/contact-chat'
import { type MiniProgram, MiniProgramType } from '@/services/resources/mini-programs'
import { type Videonum, VideonumType } from '@/services/resources/videonums'
import { chatRecordsChoosingSubject } from '../records'
import s from './actions.module.less'
import { modals } from './modals'
import { type RecordContact } from './index'

export function Actions<T extends Type>(props: {
  isAdmin: boolean
  record: ChatRecordMap[T]
  sender: { name: string; avatar: string }
  contact: RecordContact
}) {
  const { record, isAdmin, sender, contact } = props

  const items = [
    ...commonActions.filter(item => item.visible(record, contact, isAdmin)),
    ...(actionsOfType[record.type] ?? []),
  ].filter(item => !(item.admin ?? false) || isAdmin) as ActionOfType<Type>[]

  const [isOpen, setIsOpen] = useState(false)
  const close = useCallback(() => {
    setIsOpen(false)
    document.removeEventListener('click', close)
  }, [])
  const open = useCallback(() => {
    setIsOpen(true)
    setTimeout(() => document.addEventListener('click', close))
  }, [])

  if (!items.length) return null

  const itemsElm = <>
    {items.map((item, index) => <a
      key={index}
      onClick={() => item.execute(record, sender, contact)}
    >
      {item.name}
    </a>)}
  </>

  return <>
    <Tooltip
      open={isOpen}
      placement={record.isSend ? 'leftTop' : 'rightTop'}
      overlayClassName={s['actions']}
      title={itemsElm}
    >
      <div styleName="actions-toggle" onClick={!isOpen ? open : undefined}>
        <EllipsisOutlined />
      </div>
    </Tooltip>
  </>
}

// =============================================

interface ActionOfType<T extends Type> {
  name: string
  execute: (
    record: ChatRecordMap[T],
    sender: { name: string; avatar: string },
    contact: RecordContact,
  ) => void
  admin?: boolean // 是否需要管理员权限，默认为 false
}

const actionsOfType: Partial<{ [T in Type]: ActionOfType<T>[] }> = {
  [Type.Emotion]: [{ name: '添加到表情', execute: record => void addEmotion(record.content.url) }],
  [Type.MiniProgram]: [
    {
      name: '设为我的小程序',
      execute: record => void addMiniprogram(MiniProgramType.Personal, record.content),
    },
    {
      name: '设为全员小程序',
      execute: record => void addMiniprogram(MiniProgramType.Global, record.content),
      admin: true,
    },
  ],
  [Type.Videonum]: [
    {
      name: '设为我的视频号',
      execute: record => void addVideonum(VideonumType.Personal, record.content),
    },
    {
      name: '设为全员视频号',
      execute: record => void addVideonum(VideonumType.Global, record.content),
      admin: true,
    },
  ],
}

async function addEmotion(url: string) {
  const resp = await services.resources.emotions.add(url)
  if (resp.success) void message.success('表情添加成功')
  else void message.success(`表情添加失败：${resp.error}`)
}

async function addMiniprogram(type: MiniProgramType, content: MiniProgram) {
  const typeName = type === MiniProgramType.Global ? '全员' : '个人'
  const resp = await services.resources.miniPrograms.add(type, content)
  if (resp.success) void message.success(`${typeName}小程序添加成功`)
  else void message.warning(`${typeName}小程序添加失败：${resp.error}`)
}

async function addVideonum(type: VideonumType, content: Videonum) {
  const typeName = type === VideonumType.Global ? '全员' : '个人'
  const resp = await services.resources.videonums.add(type, content)
  if (resp.success) void message.success(`${typeName}视频号添加成功`)
  else void message.warning(`${typeName}视频号添加失败：${resp.error}`)
}

// =============================================

interface CommonAction extends ActionOfType<Type> {
  visible: (record: ChatRecord, contact: RecordContact, isAdmin: boolean) => boolean
}

const commonActions: CommonAction[] = [
  {
    visible(record: ChatRecord) {
      return services.chat.isRecallable(record)
    },
    name: '撤回',
    execute: recall,
  },
  {
    name: '转发',
    execute(record: ChatRecord) {
      modals.forward.next([record])
    },
    visible: isForwardable,
  },
  {
    name: '添加到我的话术',
    execute(record: ChatRecord) {
      void services.resources.wordsLibrary.addFromChat(record, false)
    },
    visible: IsCompatibleWithWord,
  },
  {
    name: '添加到全员话术',
    execute(record: ChatRecord) {
      void services.resources.wordsLibrary.addFromChat(record, true)
    },
    visible: IsCompatibleWithWord,
    admin: true,
  },
  {
    name: '设为机器人语料',
    execute(record: ChatRecord, sender: unknown, contact: RecordContact) {
      if (record.type !== Type.Text) return
      const config = services.features.robot.configs$.value.get(contact.ownerWxid)
      if (!config) return void message.warning('机器人语料保存失败，当前没有生效的企微分组')

      // 收集最近 10 条聊天记录，来总结场景
      const history = services.chat.history.value?.items ?? []
      let index = history.indexOf(record)
      const relatedRecords: TextRecord[] = []
      while (index >= 0 && relatedRecords.length < 10) {
        const record = history[index]
        if (record && record.type === Type.Text) relatedRecords.unshift(record)
        index--
      }

      modals.saveToRobot.next({
        profileId: config.profileId,
        records: relatedRecords.map(v => ({ isReceive: !v.isSend, text: v.content.text })),
      })
    },
    visible(record: ChatRecord, contact: RecordContact) {
      const hasGroup = !!services.features.robot.configs$.value.get(contact.ownerWxid)
      return record.isSend && record.type === Type.Text && hasGroup
    },
  },
  {
    name: '引用',
    execute(record: ChatRecord, sender) {
      const history = services.chat.history.value?.items ?? []
      if (record.isSend && record.id === history[history.length - 1]?.id) {
        return message.warning('暂不支持引用自己发送的最新一条消息')
      }
      void services.chat.quote(record, sender.name)
    },
    visible: canQuote,
  },
  {
    name: '复制',
    execute(record: ChatRecord) {
      if (record.type !== Type.Text) return
      const text = record.content.text
      copy(text)
    },
    visible(record) {
      return [Type.Text].includes(record.type)
    },
  },
  {
    name: '多选',
    execute(record: ChatRecord) {
      chatRecordsChoosingSubject.next(record)
    },
    visible: isForwardable,
  },
]

/**
 * 指定聊天记录是否可转换成“话术“
 * 支持转换的列表来自 wordsLibrary.addFromChat()
 */
const wordCompatibleTypes = new Set([
  Type.Text,
  Type.Image,
  Type.Video,
  // Type.Audio,  // 音频会触发风控，暂时禁用
  Type.File,
  Type.LinkCard,
  Type.MiniProgram,
  Type.Videonum,
])
export function IsCompatibleWithWord(record: ChatRecord) {
  return wordCompatibleTypes.has(record.type)
}

/**
 * 判断聊天记录是否可“转发”
 * 大体上，支持调用 services/contact-chat/api/send() 发送的消息就可以转发
 * 不过部分消息因业务原因不允许
 */
const forwardableTypes = new Set([
  Type.Text,
  Type.Image,
  Type.Video,
  // Type.Audio,  // 音频会触发风控，暂时禁用
  Type.File,
  Type.Emotion,
  Type.LinkCard,
  Type.MiniProgram,
  Type.Videonum,
])
export function isForwardable(record: ChatRecord) {
  return forwardableTypes.has(record.type)
}

async function recall(record: ChatRecord) {
  const res = await services.chat.recall(record.id)
  if (res.success) void message.success('撤回成功')
  else void message.warning(`撤回失败：${res.error}`)
}

export function canQuote(record: ChatRecord) {
  return [
    Type.Text,
    Type.Image,
    Type.Video,
    Type.Audio,
    Type.File,
    Type.Emotion,
    Type.LinkCard,
    Type.MiniProgram,
    Type.Videonum,
    Type.Position,
  ].includes(record.type)
}
export function doQuote(record: ChatRecord, sender: { name: string }) {
  const history = services.chat.history.value?.items ?? []
  if (record.isSend && record.id === history[history.length - 1]?.id) {
    return message.warning('暂不支持引用自己发送的最新一条消息')
  }
  void services.chat.quote(record, sender.name)
}
